/* Includes ------------------------------------------------------------------*/
#include <stdio.h>
#include <stddef.h>
#include <string.h>

#include "shell.h"

#include "tx_api.h"
#include "ux_api.h"
#include "ux_device_class_cdc_acm.h"
#include "main.h"

/* Private includes ----------------------------------------------------------*/

/* Private define ------------------------------------------------------------*/
#define TASK_STACK_SIZE 2048

/* Private typedef -----------------------------------------------------------*/

/* Private macro -------------------------------------------------------------*/

/* Global variables ---------------------------------------------------------*/

/* External function ---------------------------------------------------------*/

/* External variables ---------------------------------------------------------*/
extern UX_SLAVE_CLASS_CDC_ACM *cdc1;

/* Private variables ---------------------------------------------------------*/
static TX_THREAD tx_task;
static uint8_t task_stack[TASK_STACK_SIZE];

static Shell shell;
static char shell_buffer[1024];
static uint8_t recv_buf[512];

/* Private function prototypes -----------------------------------------------*/
static void task_entry(ULONG arg);

/* Public function prototypes -----------------------------------------------*/

/* Private Function definitions -----------------------------------------------*/
static signed short shell_write(char *ch, unsigned short len)
{
    ULONG actual_length;
    ux_device_class_cdc_acm_write(cdc1, (UCHAR *)ch, len, &actual_length);
    return 0;
}


static void task_entry(ULONG arg)
{
    (void)arg;
    ULONG actual_length;
    ULONG ux_status = UX_SUCCESS;

    shell.write = shell_write;
    shellInit(&shell, shell_buffer, 512);

    while (1)
    {
        if (cdc1 == UX_NULL)
        {
            tx_thread_sleep(100);
            continue;
        }

        ux_status = ux_device_class_cdc_acm_read(cdc1, (UCHAR *)&recv_buf, 1, &actual_length);
        if (ux_status != UX_SUCCESS)
            continue;

        for (int i = 0; i < actual_length; i++)
        {
            shellHandler(&shell, recv_buf[i]);
        }
    }
}

static void software_reset()
{
    NVIC_SystemReset();
}

static uint32_t device()
{
    return 0x43414d00; // ASCII 'OCM'
}

static uint32_t version()
{
    return 0x00000001; // u16+u16: big version + little version
}

/* Public Function definitions -----------------------------------------------*/
void shell_init()
{
    UINT ret;
    ret = tx_thread_create(&tx_task, "acm-shell", task_entry, 0,
                           task_stack, TASK_STACK_SIZE,
                           20u, 20u, TX_NO_TIME_SLICE, TX_AUTO_START);
    if (ret != TX_SUCCESS)
    {
        Error_Handler();
    }
}
/* Testbench ------------------------------------------------------------*/

/****************** letter shell register *********************************/
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC), reset, software_reset, system reset);
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC), device, device, device name);
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC), version, version, software version);
